Skip to content

fix(Datastore): Create a plaintext transport channel when using the Emulator#12721

Open
mw-ship-it wants to merge 1 commit intogoogleapis:mainfrom
mw-ship-it:patch-1
Open

fix(Datastore): Create a plaintext transport channel when using the Emulator#12721
mw-ship-it wants to merge 1 commit intogoogleapis:mainfrom
mw-ship-it:patch-1

Conversation

@mw-ship-it
Copy link
Copy Markdown

Problem Statement

When using the java-datastore SDK with GrpcTransportOptions and connecting to a local Datastore Emulator (e.g., via the DATASTORE_EMULATOR_HOST environment variable), the client consistently throws an UNAUTHENTICATED: Request is missing required authentication credential error.

The Root Cause

In GrpcDatastoreRpc.java, the SDK constructor detects the emulator correctly and creates an unauthenticated, plaintext gRPC channel specifically tailored for local development via getClientContextForEmulator(datastoreOptions).

However, right after generating this specialized context, the SDK uses a builder (DatastoreStubSettings.newBuilder) that unconditionally overwrites the transport channel provider with a default production-oriented channel provider in order to set channel pooling limits.

Because the emulator's custom local channel gets overwritten by this production channel configuration, the SDK ultimately discards the emulator settings and attempts to connect securely to the actual production endpoint (datastore.googleapis.com), but without any credentials (since it knows it's an emulator). Production immediately rejects the call.

The Fix

The fix converts the monolithic DatastoreStubSettings builder chain into a sequential one, and conditionally applies the custom channel pooling provider only if the environment is not an emulator:

      DatastoreStubSettings.Builder datastoreStubSettingsBuilder =
          DatastoreStubSettings.newBuilder(clientContext)
              .applyToAllUnaryMethods(retrySettingSetter(datastoreOptions));

      if (!isEmulator(datastoreOptions)) {
        datastoreStubSettingsBuilder.setTransportChannelProvider(
           // ... Production connection pool settings
        );
      }

Why this is a safe fix without side effects

  1. Zero Impact on Production Traffic: The fix relies exclusively on the existing, battle-tested isEmulator() method. If the SDK is pointed at production, !isEmulator returns true, and the code path executes the exact same transport channel override as before. Production throughput, connection pooling, and auth behavior remain 100% untouched.

  2. Restores Original Intent: For emulator traffic, the channel provider override is gracefully skipped. This allows the custom, unauthenticated local gRPC channel created specifically for the emulator in getClientContextForEmulator() to survive and be used by the GrpcDatastoreStub.

  3. Architecturally Safe: It introduces no new dependencies, requires no new variables or complex state management, and makes no changes to the public API surface. It simply resolves an unintentional clobbering of configuration variables at object instantiation time.

## Problem Statement

When using the `java-datastore` SDK with `GrpcTransportOptions` and connecting
to a local Datastore Emulator (e.g., via the `DATASTORE_EMULATOR_HOST`
environment variable), the client consistently throws an `UNAUTHENTICATED:
Request is missing required authentication credential` error.

### The Root Cause

In `GrpcDatastoreRpc.java`, the SDK constructor detects the emulator correctly
and creates an unauthenticated, plaintext gRPC channel specifically tailored for
local development via `getClientContextForEmulator(datastoreOptions)`.

However, right after generating this specialized context, the SDK uses a builder
(`DatastoreStubSettings.newBuilder`) that **unconditionally overwrites** the
transport channel provider with a default production-oriented channel provider
in order to set channel pooling limits.

Because the emulator's custom local channel gets overwritten by this production
channel configuration, the SDK ultimately discards the emulator settings and
attempts to connect securely to the actual production endpoint
(`datastore.googleapis.com`), but without any credentials (since it knows it's
an emulator). Production immediately rejects the call.

## The Fix

The fix converts the monolithic `DatastoreStubSettings` builder chain into a
sequential one, and conditionally applies the custom channel pooling provider
only if the environment is **not** an emulator:

```java
      DatastoreStubSettings.Builder datastoreStubSettingsBuilder =
          DatastoreStubSettings.newBuilder(clientContext)
              .applyToAllUnaryMethods(retrySettingSetter(datastoreOptions));

      if (!isEmulator(datastoreOptions)) {
        datastoreStubSettingsBuilder.setTransportChannelProvider(
           // ... Production connection pool settings
        );
      }
```

## Why this is a safe fix without side effects

1.  **Zero Impact on Production Traffic:** The fix relies exclusively on the
    existing, battle-tested `isEmulator()` method. If the SDK is pointed at
    production, `!isEmulator` returns true, and the code path executes the exact
    same transport channel override as before. Production throughput, connection
    pooling, and auth behavior remain 100% untouched.

2.  **Restores Original Intent:** For emulator traffic, the channel provider
    override is gracefully skipped. This allows the custom, unauthenticated
    local gRPC channel created specifically for the emulator in
    `getClientContextForEmulator()` to survive and be used by the
    `GrpcDatastoreStub`.

3.  **Architecturally Safe:** It introduces no new dependencies, requires no new
    variables or complex state management, and makes no changes to the public
    API surface. It simply resolves an unintentional clobbering of configuration
    variables at object instantiation time.
@mw-ship-it mw-ship-it requested a review from a team as a code owner April 8, 2026 19:16
Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request updates the GrpcDatastoreRpc class to conditionally configure the gRPC connection pool, ensuring it is only set when not running in an emulator environment. Additionally, the DatastoreStubSettings.Builder has been renamed for clarity. The review feedback recommends removing the descriptive comment for the connection pool configuration to align with repository standards regarding stale or misplaced comments.

Comment on lines +78 to +82
/* For non-emulator, configure default gRPC Connection pool with minChannelCount = 1 */
DatastoreStubSettings.Builder datastoreStubSettingsBuilder =
DatastoreStubSettings.newBuilder(clientContext)
.applyToAllUnaryMethods(retrySettingSetter(datastoreOptions))
.setTransportChannelProvider(
DatastoreSettings.defaultGrpcTransportProviderBuilder()
.setChannelPoolSettings(
ChannelPoolSettings.builder()
.setInitialChannelCount(DatastoreOptions.INIT_CHANNEL_COUNT)
.setMinChannelCount(DatastoreOptions.MIN_CHANNEL_COUNT)
.build())
.build());

datastoreStub = GrpcDatastoreStub.create(builder.build());
.applyToAllUnaryMethods(retrySettingSetter(datastoreOptions));
if (!isEmulator(datastoreOptions)) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The comment describing the connection pool configuration is misplaced and refers to a specific implementation context. In accordance with repository rules, comments that refer to stale or misplaced implementations should be removed rather than updated or moved to a different block.

      DatastoreStubSettings.Builder datastoreStubSettingsBuilder =
          DatastoreStubSettings.newBuilder(clientContext)
              .applyToAllUnaryMethods(retrySettingSetter(datastoreOptions));
      if (!isEmulator(datastoreOptions)) {
References
  1. Remove comments that refer to stale implementations instead of updating them.

@lqiu96 lqiu96 requested review from jinseopkim0 and lqiu96 April 8, 2026 21:38
@lqiu96 lqiu96 changed the title fix: Not break gRPC transport for emulator. fix(Datastore): Create a plaintext transport channel when using the Emulator Apr 8, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant